package com.dh.convert.node;

import com.dh.convert.constants.Constants;
import com.dh.convert.constants.TypeEnum;
import com.dh.convert.mapping.BeanMappingObject;
import com.dh.convert.utils.ClassUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Predicate;
import org.apache.commons.lang3.StringUtils;
import org.reflections.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class ObjectNode extends AbstractNode {

	@Override
	public TreeNode bulid(Field f) {
		super.bulid(f);
		this._isObject = Boolean.TRUE;
		return bulidChlidNode(f.getType());
	}

	public TreeNode bulidRootNode(Class<?> cls) {
		this.rootClass = cls;
		this._isRoot = Boolean.TRUE;
		this.typeName = cls.getSimpleName();
		childNodes.put(TypeEnum.CONTEXT.getFlag(), new ContextNode().build().setParent(this));
		return bulidChlidNode(rootClass);
	}

	@Override
	public Object genObject(JsonNode jn, Object targetObject, Object currentObject, Map<String, Object> context)
			throws InstantiationException, IllegalAccessException {
		Object o = super.genObject(jn, targetObject, currentObject, context);
		for (Entry<TreeNode, BeanMappingObject> e : mappingChildNodes.entrySet()) {
			BeanMappingObject bmo = e.getValue();
			TreeNode currentNode = e.getKey();
			Object value = null;
			if (bmo != null) {
				value = currentNode.getTargetValue(jn, bmo, targetObject, context, null);
				if (value != null) {
					currentNode.setValue(o, value);
				}
			}
			if (currentNode.isContext()) {
				currentNode.genObject(jn, targetObject, value, context);
			} else if (currentNode.isNestIng()) {
				currentNode.setValue(o, currentNode.genObject(jn, targetObject, value, context));
			}
		}
		return o;
	}

	public TreeNode bulidChlidNode(Class<?> cls) {
		Set<Field> fields = ReflectionUtils.getAllFields(cls, new Predicate<Field>() {
			@Override
			public boolean apply(Field input) {
				return input != null && !(input.getType() == ObjectMapper.class);
			}
		});

		for (Field field : fields) {
			field.setAccessible(Boolean.TRUE);
			Class<?> childCls = field.getType();

			if (ClassUtils.isMap(childCls)) {
				childNodes.put(field.getName(), new MapNode().bulid(field).setParent(this));
				continue;
			}
			if (ClassUtils.isCommon(childCls)) {
				childNodes.put(field.getName(), new ValueNode().bulid(field).setParent(this));
				continue;
			}
			if (ClassUtils.isColltion(childCls)) {
				childNodes.put(field.getName(), new CollectionNode().bulid(field).setParent(this));
				continue;
			}
			if (field.getType().isArray()) {
				childNodes.put(field.getName(), new ArrayNode().bulid(field).setParent(this));
				continue;
			}
			if (field.getType().isEnum()) {
				childNodes.put(field.getName(), new EnumNode().bulid(field).setParent(this));
				continue;
			}
			childNodes.put(field.getName(), new ObjectNode().bulid(field).setParent(this));
		}
		return this;
	}

	@Override
	public TreeNode getNode(String path) {
		int index = path.indexOf(Constants.POINT);
		if (index == -1) {
			return childNodes.get(path);
		} else {
			String s1 = path.substring(0, index);
			TreeNode node = childNodes.get(s1);
			if (node == null) {
				return null;
			}
			String s2 = path.substring(index + 1);
			return node.getNode(s2);
		}
	}

	@Override
	public void bulidMappingNode(String path, BeanMappingObject bmo) {
		try {
			int index = path.indexOf(Constants.POINT);
			if (index == -1) {
				TreeNode node = childNodes.get(path);
				if (node != null) {
					if (StringUtils.isBlank(bmo.getType())
							&&bmo.getTypeClass() == null
							&& node.getCurrentClass()!=null) {
						bmo.setType(node.getCurrentClass().getName());
						bmo.setTypeClass(node.getCurrentClass());
					} else {
						Class<?> typeClass = ClassUtils.getTypeClass(bmo.getType());
						if (typeClass != null) {
							bmo.setTypeClass(typeClass);
						}
					}
					if (node.isEnum()) {
						EnumNode.class.cast(node).checkNode(bmo);
						bmo.setType(node.getCurrentClass().getName());
					}
					bmo._castDefaultValue();
					mappingChildNodes.put(node, bmo);
				}
			} else {
				String s1 = path.substring(0, index);
				TreeNode node = childNodes.get(s1);
				if (node != null) {
					node.isNestIng(Boolean.TRUE);
					if ("Y".equals(bmo.getCollectionMapping())) {
						node.isCollectionMapping(Boolean.TRUE);
					}
					if (node.isCollectionMapping() && node.getSrcPath() == null) {
						node.setSrcPath(bmo.getSrcField());
					}
					node.setSrcListName(bmo.getSrcList());
					String s2 = path.substring(index + 1);
					node.bulidMappingNode(s2, bmo);
					mappingChildNodes.putIfAbsent(node, null);
				}
			}
		} catch (Exception e) {
			throw new IllegalArgumentException("bulidMappingNode error" + bmo.toString(), e);
		}
	}

	@Override
	public String toString() {
		return " [ nodeName=" + nodeName + " type=" + typeName + " parentNode=" + parentNodeName + " childNodes="
				+ childNodes + "]";
	}

}
